/**
 * JAVACC DEMO 1.0
 * @copy right dwusoft company All rights reserved. 
 * @Package com.apache.svm.storage.service.plugins  
 */
package com.apache.funds.service.plugins;

import java.util.Date;
import java.util.List;
import java.util.Map;

import net.sf.json.JSONObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.apache.api.manager.PluginConnector;
import com.apache.api.vo.ParamsVo;
import com.apache.api.vo.ResultEntity;
import com.apache.cache.util.Validator;
import com.apache.funds.common.FundsCache;
import com.apache.funds.common.FundsUtil;
import com.apache.funds.entity.SvmFlowing;
import com.apache.funds.entity.SvmVoucher;
import com.apache.funds.entity.SvmVoucherDetail;
import com.apache.funds.manager.SvmVoucherDetailManager;
import com.apache.funds.manager.SvmVoucherManager;
import com.apache.funds.util.Constants;
import com.apache.funds.util.FundsTemplate;
import com.apache.tools.DateUtils;
import com.apache.tools.DateUtils.FmtStr;
import com.apache.tools.StrUtil;

/**
 * 本类主要涉及业务逻辑<br>
 * 1.所有资金写操作的公共接口（后续推荐使用此统一接口）<br>
 * 1.所有Voucher主表和子表按模板的都在此类处理：(出入金，冻结，解冻，交割等）<br>
 * @author xhe 创建时间：2015-12-08  
 * 
 */
public class FundsPublicOperatePlugin implements PluginConnector {
	private Logger log = LoggerFactory.getLogger(getClass());
	private SvmVoucherManager svmVoucherManager;
	private SvmVoucherDetailManager svmVoucherDetailManager;
	private Map<String, PluginConnector> plugins;

	public void setPlugins(Map<String, PluginConnector> plugins) {
		this.plugins = plugins;
	}

	public void setSvmVoucherDetailManager(SvmVoucherDetailManager svmVoucherDetailManager) {
		this.svmVoucherDetailManager = svmVoucherDetailManager;
	}

	public void setSvmVoucherManager(SvmVoucherManager svmVoucherManager) {
		this.svmVoucherManager = svmVoucherManager;
	}

	/**
	 * TODO 对于资金操作的公共接口  
	 * @see com.apache.api.manager.PluginConnector#execute(com.apache.api.vo.ParamsVo)  
	 */
	@Override
	public Object execute(ParamsVo vo) throws Exception {
		// 对于底层插库的方法是统一的，为了满足要求这里对(出入金，冻结，解冻，交割等）
		ResultEntity entity = new ResultEntity();
		entity.setResult("true");
		entity.setEntity("false");
		entity.setMessage(Constants.Msg.F001);

		//1）获取模板参数
		List<Map<String, String>> list = (List<Map<String, String>>) vo.getParams("funds");
		String actionId = (String) vo.getParams("actionId"); // 交易流水号
		String remark = (String) vo.getParams("remark");

		long dateTrade = DateUtils.parse(DateUtils.format(DateUtils.getDate(), "yyyyMMdd"), "yyyyMMdd").getTime();//Long.valueOf(String.valueOf(vo.getParams("dateTrade"))); // 此处需要将dateTrade转为日期（以前是从前面传入的参数）

		String tradeType = null;
		String fromUserId = null;
		String toUserId = null;
		Map<String, String> data = null;

		// 此为存储ID，这里的ID先用此方式存储，将来将从发表表获取
		String voucherId = Validator.generate();
		for (int i = 0; i < list.size(); i++) {
			//2）循环获取每个模板，进行业务操作
			data = list.get(i);
			tradeType = data.get("tradeType");//操作模板类型
			long amount = 0;
			String amt = null;
			amt = data.get("amt");
			if (StrUtil.isNull(tradeType) || StrUtil.isNull(amt)) {
				entity.setMessage(Constants.Msg.F006);
				return entity;
			}
			boolean isMySelfOpt = true;
			if (Validator.isNotNull(data.get("userId"))) {
				fromUserId = data.get("userId");
				toUserId = data.get("userId");
				isMySelfOpt = true;//自己操作自己资金
				//验证此用户是否开通账户
				ResultEntity re = FundsUtil.selectFundsMyAccount(fromUserId);
				if (re != null && "false".equals(re.getEntity())) {
					entity.setMessage(Constants.Msg.F004 + fromUserId);
					return entity;
				}
			} else {
				fromUserId = data.get("fromUserId");
				toUserId = data.get("toUserId");
				isMySelfOpt = false;//非自己内部资金移动（钱给别人了）

				//验证此买卖双方用户是否开通账户
				ResultEntity re = FundsUtil.selectFundsMyAccount(fromUserId);
				if (re != null && "false".equals(re.getEntity())) {
					entity.setMessage(Constants.Msg.F004 + fromUserId);
					return entity;
				}
				re = FundsUtil.selectFundsMyAccount(toUserId);
				if (re != null && "false".equals(re.getEntity())) {
					entity.setMessage(Constants.Msg.F004 + toUserId);
					return entity;
				}
			}

			//3）转换金额，防止格式错误
			try {
				amount = Long.valueOf(amt);
			} catch (Exception e) {
				e.printStackTrace();
				log.info("操作金额{" + amount + "}类型转换失败");
				entity.setMessage(Constants.Msg.F007 + "（操作金额）");
				return entity;
			}
			vo.setParams("tradeType", tradeType);
			vo.setParams("amount", amount);
			//			// 判断资金情况
			//			this.operateCaptionBalance(fromUserId, toUserId, dateTrade, tradeType, data);

			//4）判断用户资金是否可移动
			this.checkFunds(entity, fromUserId, toUserId, vo);
			if ("false".equals(entity.getEntity())) {
				return entity;
			}

			//下面就是开始数据保存
			//5）保存业务模板操作主表
			if (i == 0) {
				SvmVoucher voucher = new SvmVoucher();
				voucher.setTradeRecordId(actionId);
				voucher.setVoucherId(voucherId);
				voucher.setAmt(amount);
				voucher.setDateTrade(dateTrade);
				voucher.setRemark(remark);
				voucher.setTimeTrade(new Date().getTime());//保存毫秒时间戳
				voucher.setTradeType(tradeType);//保存模板科目类型
				ParamsVo<SvmVoucher> voucherVo = new ParamsVo<SvmVoucher>();
				voucherVo.setObj(voucher);
				svmVoucherManager.saveInfo(voucherVo);
			}

			//6）根据模板保存科目子表
			this.operateSub(voucherId, dateTrade, amount, fromUserId, toUserId, tradeType);

			//7）保存账户操作的流水；（单向操作）
			if (isMySelfOpt) {
				if (Constants.TradeType.FUNDS_INTO.equals(tradeType) || Constants.TradeType.CANCEL_BUY_ORDER.equals(tradeType) || Constants.TradeType.UNLOCK_TN.equals(tradeType) ) {
					//可用资金增加（入金，解冻，解锁T+N）
					this.saveFlowing(fromUserId, "", amount, Constants.Description.IN, tradeType, actionId, remark);
				} else if (Constants.TradeType.FUNDS_OUTTO.equals(tradeType) || Constants.TradeType.PUB_BUY_ORDER.equals(tradeType) ) {
					//可用资金减少（出金，冻结）
					this.saveFlowing(fromUserId, "", amount, Constants.Description.OUT, tradeType, actionId, remark);
				} else {
					//资金不变
					this.saveFlowing(fromUserId, "", amount, "-", tradeType, actionId, remark);
				}
			} else {
				//双向操作
				this.saveFlowing(fromUserId, toUserId, amount, Constants.Description.OUT, tradeType, actionId, remark);
				this.saveFlowing(toUserId, fromUserId, amount, Constants.Description.IN, tradeType, actionId, remark);
			}
		}

		entity.setEntity("true");
		entity.setMessage(Constants.Msg.F000);
		return entity;
	}

	/**
	 * 判断资金情况
	 * 判断out方在对应科目的资金下，是否可以操作（防止多出）
	 */
	private void checkFunds(ResultEntity entity, String fromUserId, String toUserId, ParamsVo<SvmVoucher> vo)
			throws Exception {
		entity.setEntity("false");
		entity.setMessage(Constants.Msg.F008);

		// 判断out方资金
		//不是自己操作自己资金情况
		if (!fromUserId.equals(toUserId)) {
			vo.setParams("flag", Constants.Description.OUT);
			vo.setParams("userId", fromUserId);
		} else {
			// 判断资金情况 当fromUser 与 toUser相同时只调一次
			// 判断自己的只是自己内部资金的移动情况，不会出现资金上下限的问题
			vo.setParams("flag", Constants.Description.OUT);
			vo.setParams("userId", fromUserId);
		}
		ResultEntity reOut = (ResultEntity) plugins.get("fundsLimit").execute(vo);
		log.info("FundsLimitPlugin中[" + fromUserId + "]资金规则验证结果：" + JSONObject.fromObject(reOut));

		if ("false".equals(reOut.getEntity())) {
			entity.setEntity("false");
			entity.setMessage(reOut.getMessage());
		} else {
			entity.setEntity("true");
			entity.setMessage(Constants.Msg.F009);
		}
	}

	/**
	 * 插入子表voucherDetail
	 */
	private void operateSub(String voucherId, long dateTrade, long amt, String fromUser, String toUser, String tradeType)
			throws Exception {
		// 1.获取模板,目前先用DataUtis的getStorageTemplate方法获取，将来可以将模板数据存至数据库中
		List<FundsTemplate> templates = null;
		templates = Constants.getStorageTemplate(tradeType).get(tradeType);
		String recordId = null;
		for (FundsTemplate ft : templates) {
			// 此为存储ID，这里的ID先用此方式存储，将来将从发表表获取
			recordId = Validator.generate();
			SvmVoucherDetail voucherDetail = new SvmVoucherDetail();
			voucherDetail.setVoucherId(voucherId);
			voucherDetail.setVoucherDetailNum(recordId);
			voucherDetail.setDateTrade(dateTrade);
			voucherDetail.setCaptionId(ft.getCaption());
			if (ft.getDirection().equalsIgnoreCase(Constants.Description.OUT)) {
				voucherDetail.setUserId(fromUser);
			} else {
				voucherDetail.setUserId(toUser);
			}
			voucherDetail.setDirection(ft.getDirection());
			voucherDetail.setAmt(amt);
			voucherDetail.setState(Constants.STATUS.EFFECTIVE);
			ParamsVo pv = new ParamsVo();
			pv.setObj(voucherDetail);

			svmVoucherDetailManager.saveInfo(pv);
			//刷新user余额缓存
			FundsCache.getInstance().updateBalanCache(voucherDetail.getUserId(), ft.getCaption(), ft.getDirection(),
					amt);
		}
	}

	//
	//	//判断期初表是否有数据，若没有当前人科目就初始化一条
	//	private void operateCaptionBalance(String fromUserId, String toUserId, long dateTrade, String tradeType,
	//			Map<String, String> data) throws Exception {
	//		ParamsVo<SvmCaptionBalance> tempVo = new ParamsVo<SvmCaptionBalance>();
	//		List<FundsTemplate> templates = null;
	//		templates = Constants.getStorageTemplate(tradeType).get(tradeType);
	//		String recordId = null;
	//		Date d = DateUtils.fromLong(dateTrade);
	//		Date yd = DateUtils.beforeNDays(d, 1);
	//		long yesterDaydateTrade = DateUtils.toLong(yd);
	//		if (Validator.isNotNull(data.get("userId"))) {
	//			for (FundsTemplate ft : templates) {
	//				tempVo.setParams("userId", String.valueOf(data.get("userId")));
	//				tempVo.setParams("captionId", ft.getCaption());
	//				tempVo.setParams("fg", "query");
	//				Object obj = plugins.get("svmCaptionBalance").execute(tempVo);
	//				if (obj != null) {
	//					continue;
	//				}
	//				SvmCaptionBalance cb = new SvmCaptionBalance();
	//				cb.setBalance(0l);
	//				cb.setCaptionId(ft.getCaption());
	//				cb.setDateTrade(yesterDaydateTrade);
	//				cb.setUserId(String.valueOf(data.get("userId")));
	//				tempVo.setParams("fg", "save");
	//				tempVo.setObj(cb);
	//				obj = plugins.get("svmCaptionBalance").execute(tempVo);
	//			}
	//		} else {
	//			for (FundsTemplate ft : templates) {
	//				tempVo.setParams("fromUserId", fromUserId);
	//				tempVo.setParams("captionId", ft.getCaption());
	//				tempVo.setParams("fg", "query");
	//				Object obj = plugins.get("svmCaptionBalance").execute(tempVo);
	//				if (obj != null) {
	//					continue;
	//				}
	//				SvmCaptionBalance cb = new SvmCaptionBalance();
	//				cb.setBalance(0l);
	//				cb.setCaptionId(ft.getCaption());
	//				cb.setDateTrade(yesterDaydateTrade);
	//				cb.setUserId(fromUserId);
	//				tempVo.setParams("fg", "save");
	//				tempVo.setObj(cb);
	//				obj = plugins.get("svmCaptionBalance").execute(tempVo);
	//			}
	//			for (FundsTemplate ft : templates) {
	//				tempVo.setParams("toUserId", toUserId);
	//				tempVo.setParams("captionId", ft.getCaption());
	//				tempVo.setParams("fg", "query");
	//				Object obj = plugins.get("svmCaptionBalance").execute(tempVo);
	//				if (obj != null) {
	//					continue;
	//				}
	//				SvmCaptionBalance cb = new SvmCaptionBalance();
	//				cb.setBalance(0l);
	//				cb.setCaptionId(ft.getCaption());
	//				cb.setDateTrade(yesterDaydateTrade);
	//				cb.setUserId(toUserId);
	//				tempVo.setParams("fg", "save");
	//				tempVo.setObj(cb);
	//				obj = plugins.get("svmCaptionBalance").execute(tempVo);
	//			}
	//		}
	//	}
	/**
	 * 保存流水明细
	 * @param userId 操作用户
	 * @param opUserId 对手用户（单方操作此值空）
	 * @param amount 金额
	 * @param direction 方向
	 * @param actionType 类型
	 * @param actionId 业务id
	 * @param remark 备注
	 * @throws Exception
	 */
	private void saveFlowing(String userId, String opUserId, long amount, String direction, String actionType,
			String actionId, String remark) throws Exception {
		ParamsVo<SvmFlowing> voFlowing = new ParamsVo<SvmFlowing>();
		SvmFlowing flowing = new SvmFlowing();
		flowing.setUserId(userId);
		flowing.setAmount(String.valueOf(amount));
		flowing.setDirection(direction);
		flowing.setActionType(actionType);
		flowing.setActionId(actionId);
		flowing.setActionName(remark);
		flowing.setCreateTime(DateUtils.format(DateUtils.getDate(), FmtStr.yyyyMMdd_HHmmssSSS.toString()));
		flowing.setTransDate(DateUtils.format(DateUtils.getDate(), "yyyyMMdd"));
		flowing.setOpacctId(opUserId);
		voFlowing.setObj(flowing);
		Object obj = plugins.get("fundsFlowingSave").execute(voFlowing);
	}
}
